#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#include "ExeContext.h"
#include "../BMCRepository.h"
#include "../../6502.h"

ExeContext* ECX_New(uint8_t Mask){
    ExeContext* New;
    
    New = calloc(sizeof(ExeContext), 1);
    
    New->ECXMask = Mask;
    
    return New;
}

ExeContext* ECX_NewFromCPUContext(__Context* CPUContext, uint8_t ContextMask){
    ExeContext *New;
    
    New = ECX_New(ContextMask);
    
    if(ContextMask & ECTXT_MASK_A)
        New->A = CPUContext->A;
    
    if(ContextMask & ECTXT_MASK_X)
        New->X = CPUContext->X;
    
    if(ContextMask & ECTXT_MASK_Y)
        New->Y = CPUContext->Y;    
    
    if(ContextMask & ECTXT_MASK_S)
        New->S = CPUContext->S;    
    
    if(ContextMask & ECTXT_MASK_P)
        New->P = CPUContext->P;
    
    if(ContextMask & ECTXT_MASK_PC){
        New->PC = CPUContext->PC;    
        New->SourceBMC = BMCRSearch(CPUContext->PC);
    }
    
    ECX_CalculateAndSetHash(New);
    
    return New;
}

uint64_t ECX_CalculateHash(ExeContext *EContext){
    uint64_t Hash;
    
    Hash = 0;
    
    Hash |= (uint64_t) EContext->PC;    
    Hash |= ((uint64_t) EContext->A  << 16);
    Hash |= ((uint64_t) EContext->S  << 24);
    Hash |= ((uint64_t) EContext->P  << 32);
    Hash |= ((uint64_t) EContext->X  << 40);
    Hash |= ((uint64_t) EContext->Y  << 48);

    
    return Hash;
}

void ECX_CalculateAndSetHash(ExeContext *EContext){
    
    EContext->ECXContextHash = ECX_CalculateHash(EContext);
    
}

char* ECX_GenerateContextString(ExeContext *EContext){
    
    char* ContextString;
    char  Scrap[0x80];
    
    uint32_t i, Size, Mask;
    
    Size = 0;
    
    Mask = EContext->ECXMask;
    
    while(Mask)
        Size++, Mask>>=1;
                
    ContextString = calloc(Size * 0x20, 1);

    Mask = EContext->ECXMask;
    
    /*if(Mask & ECTXT_MASK_PC){
        sprintf(Scrap, " CPU.PC = 0x%04X;", EContext->PC);
        strcat(ContextString, Scrap);
    } */       
    
    if(Mask & ECTXT_MASK_A){
        sprintf(Scrap, " CPU.A = 0x%02X;", EContext->A);
        strcat(ContextString, Scrap);
    }
    
    if(Mask & ECTXT_MASK_S){
        sprintf(Scrap, " CPU.S = 0x%02X;", EContext->S);
        strcat(ContextString, Scrap);
    }
    
    if(Mask & ECTXT_MASK_P){
        sprintf(Scrap, " CPU.P = 0x%02X;", EContext->P);
        strcat(ContextString, Scrap);
    }
    
    if(Mask & ECTXT_MASK_X){
        sprintf(Scrap, " CPU.X = 0x%02X;", EContext->X);
        strcat(ContextString, Scrap);
    }
    
    if(Mask & ECTXT_MASK_Y){
        sprintf(Scrap, " CPU.Y = 0x%02X;", EContext->Y);
        strcat(ContextString, Scrap);
    }    
    
    return ContextString; 
}

void ECX_IncrementExecutionCount(ExeContext *Context){
    Context->ExecutionCount++;
}

uint8_t  ECX_GetMaskFromChar(char MaskChar){
    switch(MaskChar){
        case 'a':
            return ECTXT_MASK_A;
        break;
        case 'x':
            return ECTXT_MASK_X;
        break;
        case 'y':
            return ECTXT_MASK_Y;
        break;
        case 's':
            return ECTXT_MASK_S;
        break; 
        case 'p':
            return ECTXT_MASK_P;
        break;  
        default:
            return 0;   
        break;
    }
}
